package com.ws.soa.consumer.service.log;

import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.rolling.RollingFileAppender;

/**
 * Date: 2019-02-13
 * Time: 20:17
 * Description: 异步日志服务
 *
 * @author qiezi
 */
public class AsyncRollingFileAppenderService extends RollingFileAppender {
    BlockingQueue<ILoggingEvent> blockingQueue;
    int queueSize = 256;
    int discardingThreshold = 0;
    int maxFlushTime = 1000;
    boolean includeCallerData = true;
    AsyncRollingFileAppenderService.Worker worker = new AsyncRollingFileAppenderService.Worker();

    public AsyncRollingFileAppenderService() {
    }

    @Override
    public void start() {
        if (this.queueSize < 1) {
            this.addError("Invalid queue size [" + this.queueSize + "]");
        } else {
            this.blockingQueue = new ArrayBlockingQueue(this.queueSize);
            this.addInfo("Setting discardingThreshold to " + this.discardingThreshold);
            this.worker.setDaemon(true);
            this.worker.setName("AsyncAppender-Worker-" + this.getName());
            super.start();
            this.worker.start();
        }
    }

    @Override
    public void stop() {
        if (this.isStarted()) {
            super.stop();
            this.worker.interrupt();

            try {
                this.worker.join((long) this.maxFlushTime);
                if (this.worker.isAlive()) {
                    this.addWarn("Max queue flush timeout (" + this.maxFlushTime + " ms) exceeded. Approximately " + this.blockingQueue.size() + " queued events were possibly discarded.");
                } else {
                    this.addInfo("Queue flush finished successfully within timeout.");
                }
            } catch (InterruptedException var2) {
                this.addError("Failed to join worker thread. " + this.blockingQueue.size() + " queued events may be discarded.", var2);
            }

        }
    }

    protected void append(ILoggingEvent eventObject) {
        if (!this.isQueueBelowDiscardingThreshold()) {
            eventObject.prepareForDeferredProcessing();
            eventObject.getCallerData();
            this.put(eventObject);
        }
    }

    private boolean isQueueBelowDiscardingThreshold() {
        return this.blockingQueue.remainingCapacity() < this.discardingThreshold;
    }

    private void put(ILoggingEvent eventObject) {
        try {
            this.blockingQueue.put(eventObject);
        } catch (InterruptedException var3) {
            ;
        }

    }

    public int getQueueSize() {
        return this.queueSize;
    }

    public void setQueueSize(int queueSize) {
        this.queueSize = queueSize;
    }

    public int getDiscardingThreshold() {
        return this.discardingThreshold;
    }

    public void setDiscardingThreshold(int discardingThreshold) {
        this.discardingThreshold = discardingThreshold;
    }

    public int getMaxFlushTime() {
        return this.maxFlushTime;
    }

    public void setMaxFlushTime(int maxFlushTime) {
        this.maxFlushTime = maxFlushTime;
    }

    public boolean isIncludeCallerData() {
        return this.includeCallerData;
    }

    public void setIncludeCallerData(boolean includeCallerData) {
        this.includeCallerData = includeCallerData;
    }

    class Worker extends Thread {
        Worker() {
        }

        @Override
        public void run() {
            AsyncRollingFileAppenderService parent = AsyncRollingFileAppenderService.this;

            while (parent.isStarted()) {
                try {
                    ILoggingEvent i$ = parent.blockingQueue.take();
                    parent.subAppend(i$);
                } catch (InterruptedException var4) {
                    break;
                }
            }

            AsyncRollingFileAppenderService.this.addInfo("Worker thread will flush remaining events before exiting. ");
            Iterator i$1 = parent.blockingQueue.iterator();

            while (i$1.hasNext()) {
                ILoggingEvent e = (ILoggingEvent) i$1.next();
                parent.subAppend(e);
                parent.blockingQueue.remove(e);
            }

        }
    }
}

